package com.mlethe.library.widget.gesturelock;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.RelativeLayout;

import com.mlethe.library.widget.gesturelock.listener.GestureEventListener;
import com.mlethe.library.widget.gesturelock.listener.GesturePasswordSettingListener;
import com.mlethe.library.widget.gesturelock.listener.GestureUnmatchedExceedListener;

import java.util.ArrayList;
import java.util.List;

/**
 * 手势
 * @author Mlethe
 */
public class GestureLockViewGroup extends RelativeLayout {
    /**
     * 保存所有的GestureLockView
     */
    private GestureLockView[] mGestureLockViews;
    /**
     * 每个边上的GestureLockView的个数
     */
    private int mCount = 3;
    /**
     * 存储密码
     */
    private String password;
    /**
     * 保存用户选中的GestureLockView的id
     */
    private List<Integer> mChoose = new ArrayList<>();
    private String mChooseString = "";

    private Paint mPaint;
    /**
     * GestureLockView的边长 4 * mWidth / ( 5 * mCount + 1 )
     */
    private float mGestureLockViewWidth;

    /**
     * 每个GestureLockView的间距
     */
    private float mMarginBetweenLockView;

    /**
     * 线条宽度
     */
    private float mLineWidth;

    /**
     * 线条透明度
     */
    private int mLineAlpha;

    /**
     * 类型
     */
    private int mLockViewType = 10000;

    /**
     * GestureLockView默认状态圆心是否显示
     */
    private boolean mLockViewDotShow;

    /**
     * 箭头大小
     */
    private float mArrowRate = -1;

    /**
     * 是否显示线条
     */
    private boolean mLineShow;

    /**
     * GestureLockView无手指触摸的状态下圆的颜色
     */
    private int mNoFingerColor = 0xFF898AA1;

    /**
     * GestureLockView手指触摸的状态下圆的颜色
     */
    private int mFingerOnColor = 0XFF3BD1CA;
    /**
     * GestureLockView手指抬起的状态下,正确时圆的颜色
     */
    private int mFingerUpColorCorrect = 0XFF3BD1CA;

    /**
     * GestureLockView手指抬起的状态下，错误圆的颜色
     */
    private int mFingerUpColorError = 0xFFFF0000;
    /**
     * 宽度
     */
    private int mWidth;
    /**
     * 高度
     */
    private int mHeight;

    private Path mPath;
    /**
     * 指引线的开始位置x
     */
    private int mLastPathX;
    /**
     * 指引线的开始位置y
     */
    private int mLastPathY;
    /**
     * 指引下的结束位置
     */
    private Point mTmpTarget = new Point();

    /**
     * 最大尝试次数
     */
    private int mTryTimes = -1;

    /**
     * 最少连接点
     */
    private int minChooseNum = 4;

    /**
     * 回调接口
     */
    private GesturePasswordSettingListener gesturePasswordSettingListener;
    private GestureEventListener gestureEventListener;
    private GestureUnmatchedExceedListener gestureUnmatchedExceedListener;

    /**
     * 密码模式，true 密码设置模式， false 密码验证模式
     */
    private boolean isPasswordSettingMode;
    private boolean isWaitForFirstInput = true;
    private boolean isRetryTimeLimit = false;
    private String firstInputPassword = "";
    private Handler mHandler = new Handler();
    private Runnable mResetRunnable = new Runnable() {
        @Override
        public void run() {
            resetView();
        }
    };
    private GestureLockView.VerifyStatus mVerifyStatus = GestureLockView.VerifyStatus.NO_VERIFY;

    public GestureLockViewGroup(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public GestureLockViewGroup(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        //获得所有自定义的参数的值
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GestureLockViewGroup, defStyle, 0);

        mGestureLockViewWidth = a.getDimension(R.styleable.GestureLockViewGroup_lockViewWidth, -1);
        mMarginBetweenLockView = a.getDimension(R.styleable.GestureLockViewGroup_marginBetweenLockView, -1);
        mLineWidth = a.getDimension(R.styleable.GestureLockViewGroup_lineWidth, -1);
        mLineAlpha = a.getInt(R.styleable.GestureLockViewGroup_lineAlpha, 50);
        mLockViewType = a.getInt(R.styleable.GestureLockViewGroup_lockViewType, mLockViewType);
        mLockViewDotShow = a.getBoolean(R.styleable.GestureLockViewGroup_lockViewDotShow, true);
        mArrowRate = a.getFloat(R.styleable.GestureLockViewGroup_lockViewArrowRate, mArrowRate);
        mLineShow = a.getBoolean(R.styleable.GestureLockViewGroup_lineShow, true);
        mNoFingerColor = a.getColor(R.styleable.GestureLockViewGroup_colorNoFinger, mNoFingerColor);
        mFingerOnColor = a.getColor(R.styleable.GestureLockViewGroup_colorFingerOn, mFingerOnColor);
        mFingerUpColorCorrect = a.getColor(R.styleable.GestureLockViewGroup_colorFingerUpCorrect, mFingerUpColorCorrect);
        mFingerUpColorError = a.getColor(R.styleable.GestureLockViewGroup_colorFingerUpError, mFingerUpColorError);
        minChooseNum = a.getInt(R.styleable.GestureLockViewGroup_minChooseNum, minChooseNum);

        a.recycle();

        // 当未设置密码，进入密码设置模式
        isPasswordSettingMode = true;

        // 初始化画笔
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPath = new Path();

        mGestureLockViews = new GestureLockView[mCount * mCount];
        for (int i = 0; i < mGestureLockViews.length; i++) {
            //初始化每个GestureLockView
            if (mLockViewType == 10001) {
                mGestureLockViews[i] = new GestureLockView2(getContext(), mNoFingerColor, mFingerOnColor, mFingerUpColorCorrect, mFingerUpColorError, mLockViewDotShow, mArrowRate);
            } else {
                mGestureLockViews[i] = new GestureLockView(getContext(), mNoFingerColor, mFingerOnColor, mFingerUpColorCorrect, mFingerUpColorError, mLockViewDotShow, mArrowRate);
            }
            mGestureLockViews[i].setId(i + 1);
            //设置参数，主要是定位GestureLockView间的位置
            LayoutParams lockerParams = new LayoutParams((int) mGestureLockViewWidth, (int) mGestureLockViewWidth);
            mGestureLockViews[i].setMode(GestureLockView.Mode.STATUS_NO_FINGER);
            addView(mGestureLockViews[i], lockerParams);
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        mWidth = MeasureSpec.getSize(widthMeasureSpec);
        if (mGestureLockViewWidth == -1) {
            // 计算每个GestureLockView的宽度
            mGestureLockViewWidth = 4 * mWidth * 1f / (5 * mCount + 1);
        }
        if (mMarginBetweenLockView == -1) {
            //计算每个GestureLockView的间距
            mMarginBetweenLockView = (mWidth - mGestureLockViewWidth * mCount) / 4;
        }
        // 设置画笔的宽度为GestureLockView的内圆直径稍微小点
        if (mLineWidth == -1) {
            mPaint.setStrokeWidth(mGestureLockViewWidth * 0.29f);
        } else {
            mPaint.setStrokeWidth(mLineWidth);
        }
        mHeight = (int) (mGestureLockViewWidth * mCount + mMarginBetweenLockView * (mCount + 1));
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(mHeight, MeasureSpec.EXACTLY);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        for (int i = 0; i < mGestureLockViews.length; i++) {
            LayoutParams lockerParams = (LayoutParams) mGestureLockViews[i].getLayoutParams();
            // 不是每行的第一个，则设置位置为前一个的右边
            if (i % mCount != 0) {
                lockerParams.addRule(RelativeLayout.RIGHT_OF, mGestureLockViews[i - 1].getId());
            }
            // 从第二行开始，设置为上一行同一位置View的下面
            if (i > mCount - 1) {
                lockerParams.addRule(RelativeLayout.BELOW, mGestureLockViews[i - mCount].getId());
            }
            //设置右下左上的边距
            float leftMargin = 0;
            float topMargin = 0;
            //每个View都有右外边距和底外边距 第一行的有上外边距 第一列的有左外边距
            if (i < mCount) {
                // 第一行
                topMargin = mMarginBetweenLockView;
            }
            if (i % mCount == 0) {
                // 第一列
                leftMargin = (mWidth - mGestureLockViewWidth * 3 - mMarginBetweenLockView * 2) / 2;
            }

            lockerParams.setMargins((int) leftMargin, (int) topMargin, (int) mMarginBetweenLockView, (int) mMarginBetweenLockView);
            mGestureLockViews[i].setLayoutParams(lockerParams);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        int x = (int) event.getX();
        int y = (int) event.getY();

        //重试次数超过限制，直接返回
        if (mTryTimes <= 0 && isRetryTimeLimit) {
            return true;
        }

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                reset();//重置
                break;
            case MotionEvent.ACTION_MOVE:
                drawAndGetSelectedWhenTouchMove(x, y);
                break;
            case MotionEvent.ACTION_UP:
                if (isPasswordSettingMode) {    // 设置密码
                    if (gesturePasswordSettingListener != null && mChoose.size() > 0) {
                        if (mChoose.size() >= minChooseNum) {
                            setPasswordHandle();//设置密码
                        } else {
                            gesturePasswordSettingListener.onFail("最少连接" + minChooseNum + "个点，请重新输入");
                        }
                    }
                } else {    // 验证密码
                    if (mChoose.size() > 0) {
                        checkAnswer();
                    } else {
                        return true;
                    }

                    if (gestureEventListener != null) {
                        boolean status = false;
                        switch (mVerifyStatus) {
                            case NO_VERIFY:
                                break;
                            case VERIFY_SUCCESS:
                                status = true;
                                break;
                            case VERIFY_ERROR:
                                break;
                        }
                        //将结果回调
                        gestureEventListener.onVerify(status, mTryTimes);
                    }
                    if (this.mTryTimes == 0) {
                        //超出重试次数，进入回调
                        gestureUnmatchedExceedListener.onUnmatchedExceedBoundary();
                    }
                }
                drawWhenTouchUp();
                break;
        }
        invalidate();
        return true;
    }

    private void drawAndGetSelectedWhenTouchMove(int x, int y) {
        mPaint.setColor(mFingerOnColor);
        mPaint.setAlpha(mLineAlpha);
        GestureLockView child = getChildIdByPos(x, y);
        if (child != null) {
            int cId = child.getId();
            if (!mChoose.contains(cId)) {
                mChoose.add(cId);
                mChooseString = mChooseString + cId;
                if (mLineShow) {
                    child.setMode(GestureLockView.Mode.STATUS_FINGER_ON);
                }
                // 设置指引线的起点
                mLastPathX = child.getLeft() / 2 + child.getRight() / 2;
                mLastPathY = child.getTop() / 2 + child.getBottom() / 2;

                if (mChoose.size() == 1) {
                    // 当前添加为第一个
                    mPath.moveTo(mLastPathX, mLastPathY);
                } else {
                    // 非第一个，将两者使用线连上
                    mPath.lineTo(mLastPathX, mLastPathY);
                }
            }
        }
        // 指引线的终点
        mTmpTarget.x = x;
        mTmpTarget.y = y;
    }

    private void drawWhenTouchUp() {
        switch (mVerifyStatus) {
            case NO_VERIFY:
                if (mChoose.size() > 0) {
                    // 将终点设置位置为起点，即取消指引线
                    mTmpTarget.x = mLastPathX;
                    mTmpTarget.y = mLastPathY;
                    invalidate();
                    mHandler.postDelayed(mResetRunnable, 500);
                }
                return;
            case VERIFY_SUCCESS:
                mPaint.setColor(mFingerUpColorCorrect);
                break;
            case VERIFY_ERROR:
                mPaint.setColor(mFingerUpColorError);
                break;
        }
        mPaint.setAlpha(mLineAlpha);
        // 将终点设置位置为起点，即取消指引线
        mTmpTarget.x = mLastPathX;
        mTmpTarget.y = mLastPathY;

        // 改变子元素的状态为UP
        setItemModeUp();
        if (mLineShow) {
            drawArrow();
        }
        mHandler.postDelayed(mResetRunnable, 500);
    }

    /**
     * 绘制箭头
     */
    private void drawArrow() {
        // 计算每个元素中箭头需要旋转的角度
        for (int i = 0; i + 1 < mChoose.size(); i++) {
            int childId = mChoose.get(i);
            int nextChildId = mChoose.get(i + 1);

            GestureLockView startChild = findViewById(childId);
            GestureLockView nextChild = findViewById(nextChildId);

            int dx = nextChild.getLeft() - startChild.getLeft();
            int dy = nextChild.getTop() - startChild.getTop();
            // 计算角度
            int angle = (int) Math.toDegrees(Math.atan2(dy, dx)) + 90;
            startChild.setArrowDegree(angle);
        }
    }

    private void setPasswordHandle() {
        if (isWaitForFirstInput) {
            gesturePasswordSettingListener.onFirstInputComplete(mChooseString);
            firstInputPassword = mChooseString;
            isWaitForFirstInput = false;
            reset();
        } else {
            if (firstInputPassword.equals(mChooseString)) {
                this.password = mChooseString;
                isPasswordSettingMode = false;
                mVerifyStatus = GestureLockView.VerifyStatus.VERIFY_SUCCESS;
                gesturePasswordSettingListener.onSuccess(firstInputPassword);
                reset();
            } else {
                mVerifyStatus = GestureLockView.VerifyStatus.VERIFY_ERROR;
                gesturePasswordSettingListener.onFail("与上一次绘制不一致，请重新输入");
            }
        }
    }

    private void setItemModeUp() {
        for (GestureLockView verticalGestureLockView : mGestureLockViews) {
            if (mChoose.contains(verticalGestureLockView.getId())) {
                verticalGestureLockView.setVerifyStatus(mVerifyStatus);
                verticalGestureLockView.setMode(GestureLockView.Mode.STATUS_FINGER_UP);
            }
        }
    }

    /**
     * 检查用户绘制的手势是否正确
     */
    public void checkAnswer() {
        if (password == null) {
            mVerifyStatus = GestureLockView.VerifyStatus.NO_VERIFY;
        } else if (password.equals(mChooseString)) {
            mVerifyStatus = GestureLockView.VerifyStatus.VERIFY_SUCCESS;
        } else {
            if (isRetryTimeLimit) {
                this.mTryTimes--;
            }
            mVerifyStatus = GestureLockView.VerifyStatus.VERIFY_ERROR;
        }
    }

    /**
     * 通过x,y获得落入的GestureLockView
     */
    private GestureLockView getChildIdByPos(int x, int y) {
        for (GestureLockView verticalGestureLockView : mGestureLockViews) {
            if (checkPositionInChild(verticalGestureLockView, x, y)) {
                return verticalGestureLockView;
            }
        }
        return null;
    }

    /**
     * 检查当前是否在child中
     */
    private boolean checkPositionInChild(View child, int x, int y) {
        //设置了内边距，即x,y必须落入下GestureLockView的内部中间的小区域中，可以通过调整padding使得x,y落入范围变大，或者不设置padding
        int padding = (int) (mGestureLockViewWidth * 0.15);

        return x >= child.getLeft() + padding && x <= child.getRight() - padding
                && y >= child.getTop() + padding
                && y <= child.getBottom() - padding;
    }

    @Override
    public void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        //绘制GestureLockView间的连线
        if (mPath != null && mLineShow) {
            canvas.drawPath(mPath, mPaint);
        }
        //绘制指引线
        if (mChoose.size() > 0 && mLineShow) {
            if (mLastPathX != 0 && mLastPathY != 0) {
                canvas.drawLine(mLastPathX, mLastPathY, mTmpTarget.x,
                        mTmpTarget.y, mPaint);
            }
        }
    }

    /**
     * 做一些必要的重置
     */
    private void reset() {
        mChoose.clear();
        mChooseString = "";
        mPath.reset();
        for (GestureLockView verticalGestureLockView : mGestureLockViews) {
            verticalGestureLockView.setMode(GestureLockView.Mode.STATUS_NO_FINGER);
            verticalGestureLockView.setArrowDegree(-1);
        }
    }

    public void setGestureEventListener(GestureEventListener gestureEventListener) {
        this.gestureEventListener = gestureEventListener;
    }

    public void setGestureUnmatchedExceedListener(int retryTimes, GestureUnmatchedExceedListener gestureUnmatchedExceedListener) {
        isRetryTimeLimit = true;
        this.mTryTimes = retryTimes;
        this.gestureUnmatchedExceedListener = gestureUnmatchedExceedListener;
    }

    public void setGesturePasswordSettingListener(GesturePasswordSettingListener gesturePasswordSettingListener) {
        this.gesturePasswordSettingListener = gesturePasswordSettingListener;
    }

    public void removePassword() {
        password = null;
        firstInputPassword = "";
        isWaitForFirstInput = true;
        isPasswordSettingMode = true;
    }

    public void setPassword(String password) {
        this.password = password;
        isPasswordSettingMode = false;
    }

    public String getPassword() {
        return password;
    }

    public void resetView() {
        reset();
        invalidate();
    }

    public void setRetryTimes(int retryTimes) {
        this.mTryTimes = retryTimes;
    }

}
